#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex.tests/mstest/log_message.h>
#include <vex.tests/mstest/crt_mem_check.h>
#include <vex.tests/mstest/tests.range/range_archetype.h>
#include <vex.tests/mstest/tests.range/forward_range_call.h>
#include <vex/iterable/concept/range_concepts.h>
#include <vex/iterable/make_raw_range.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace make_raw_range {

    namespace vc = vex::core;
    namespace vcc = vex::core::contract;
    namespace vi = vex::iterable;
    namespace vic = vex::iterable::contract;
    namespace vri = vex::iterable::implementation;

    TEST_CLASS(make_raw_range)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.make_raw_range")
            END_TEST_CLASS_ATTRIBUTE();

    public:
        template<class TRangeTraits>
        void range_is_correctly_forwarded_through_call_hierarchy_impl(
            TRangeTraits, int p_exp_forward_calls)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef decltype(vi::make_raw_range(TRangeTraits::range_type())) range_result_t;
            int t_call_count = -1;
            if (auto t_range = vi::make_raw_range(TRangeTraits::range_type())) {
                t_call_count = forward_range_call().call(t_range);
                t_range->release();
            }

            Assert::AreEqual(p_exp_forward_calls, t_call_count, wlog_message()
                << L"failed for:" << std::endl << typeid(range_result_t).name() | as_string); 
        }

        template<class TRangeHandle>
        int dispatch_explicit(TRangeHandle* p_r, vi::trivial)
        {
            return forward_range_call_explicit().call_trivial(p_r);
        }

        template<class TRangeHandle>
        int dispatch_explicit(TRangeHandle* p_r, vi::output)
        {
            return forward_range_call_explicit().call_output(p_r);
        }

        template<class TRangeHandle>
        int dispatch_explicit(TRangeHandle* p_r, vi::one_pass)
        {
            return forward_range_call_explicit().call_one_pass(p_r);
        }

        template<class TRangeHandle>
        int dispatch_explicit(TRangeHandle* p_r, vi::forward)
        {
            return forward_range_call_explicit().call_forward(p_r);
        }

        template<class TRangeHandle>
        int dispatch_explicit(TRangeHandle* p_r, vi::bidirectional)
        {
            return forward_range_call_explicit().call_bidirectional(p_r);
        }

        template<class TRangeHandle>
        int dispatch_explicit(TRangeHandle* p_r, vi::random_access)
        {
            return forward_range_call_explicit().call_random_access(p_r);
        }

        template<class TRangeTraits>
        void range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
            TRangeTraits, int p_exp_forward_calls)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef decltype(vi::make_raw_range(TRangeTraits::range_type())) range_result_t;
            int t_call_count = -1;
            if (auto t_range = vi::make_raw_range(TRangeTraits::range_type())) {
                t_call_count = this->dispatch_explicit(t_range, TRangeTraits::category());
                t_range->release();
            }

            Assert::AreEqual(p_exp_forward_calls, t_call_count, wlog_message()
                << L"failed for:" << std::endl << typeid(range_result_t).name() | as_string); 
        }

        TEST_METHOD(range_trivial_is_correctly_forwarded_through_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<> > traits;
            this->range_is_correctly_forwarded_through_call_hierarchy_impl(
                traits(), 1);
        }

        TEST_METHOD(range_trivial_is_correctly_forwarded_through_explicit_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<> > traits;
            this->range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
                traits(), 1);
        }

        TEST_METHOD(range_output_is_correctly_forwarded_through_call_hierarchy)

        {
            typedef vi::range_traits< range_archetype<vi::output_range_tag, int> > traits;
            this->range_is_correctly_forwarded_through_call_hierarchy_impl(
                traits(), 2);
        }

        TEST_METHOD(range_output_is_correctly_forwarded_through_explicit_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::output, int const&> > traits;
            this->range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
                traits(), 2);
        }

        TEST_METHOD(range_one_pass_is_correctly_forwarded_through_call_hierarchy)

        {
            typedef vi::range_traits< range_archetype<vi::one_pass_range_tag, int&> > traits;
            this->range_is_correctly_forwarded_through_call_hierarchy_impl(
                traits(), 2);
        }

        TEST_METHOD(range_one_pass_is_correctly_forwarded_through_explicit_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::one_pass, int&> > traits;
            this->range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
                traits(), 2);
        }

        TEST_METHOD(range_forward_is_correctly_forwarded_through_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::forward_range_tag, int&> > traits;
            this->range_is_correctly_forwarded_through_call_hierarchy_impl(
                traits(), 3);
        }

        TEST_METHOD(range_forward_is_correctly_forwarded_through_explicit_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::forward, int&> > traits;
            this->range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
                traits(), 3);
        }

        TEST_METHOD(range_bidirectional_is_correctly_forwarded_through_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::bidirectional_range_tag, int&> > traits;
            this->range_is_correctly_forwarded_through_call_hierarchy_impl(
                traits(), 4);
        }

        TEST_METHOD(range_bidirectional_is_correctly_forwarded_through_explicit_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::bidirectional, int&> > traits;
            this->range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
                traits(), 4);
        }

        TEST_METHOD(range_random_access_is_correctly_forwarded_through_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::random_access_range_tag, int&> > traits;
            this->range_is_correctly_forwarded_through_call_hierarchy_impl(
                traits(), 5);
        }

        TEST_METHOD(range_random_access_is_correctly_forwarded_through_explicit_call_hierarchy)
        {
            typedef vi::range_traits< range_archetype<vi::random_access, int&> > traits;
            this->range_is_correctly_forwarded_through_explicit_call_hierarchy_impl(
                traits(), 5);
        }
    };
}}